充值实现
充值(收款)和“充值后得到的积分/点数(credits)”在架构上必须严格拆开:
- 收款/支付:由支付渠道(Stripe、Apple IAP、Google Play Billing 等)负责完成与回调确认
- 积分(Credits):必须由你的业务后端与业务数据库作为唯一权威来管理(不是 Keycloak)
下面给你一套可落地、可扩展到 Web/iOS/Android 的标准实现。
1) 先定清楚:你的“积分”属于哪类商品?
你的产品是 Todo + 番茄钟,积分通常有两种含义:
- 订阅权益:Pro / Premium(按月/年)
- 可消耗点数:例如购买 1000 积分,用于解锁某些消耗型功能(AI、云同步额度、主题包等)
这两类在支付渠道规则上差别很大,尤其在 iOS/Android。
2) 支付渠道选择:Web 与移动端规则不同
Web(最常见:Stripe)
- 你的网站/网页端收款:通常用 Stripe Checkout 或 Payment Intents
- 成功后 Stripe 通过 webhook 通知你最终结果(你必须以 webhook 为准) (stripe.dev)
iOS(App Store)
- 若你在 iOS App 内售卖数字内容/服务,通常需要使用 Apple In-App Purchase(IAP) (Apple Developer) (备注:部分地区政策与外链支付规则在变化,但“数字内容用 IAP”仍是最稳妥的默认假设。) (Apple Developer)
Android(Google Play)
- Google Play 的计费与外部支付政策在不同地区/时间有变化,Google 官方近年也发布过“不再强制要求只能用 Play Billing、也不禁止其他支付方式沟通”的说明,但你仍需按上架地区与具体商品类型评估合规路径。 (Google 帮助)
务实建议:
- Web:Stripe
- iOS/Android:优先按平台内购体系做(后续再做跨平台“统一权益”)
3) 积分由谁管理?
结论
积分(credit balance)由你的业务后端 + 业务数据库管理。 Keycloak 不负责积分,也不应该存积分余额。
原因:积分属于“业务资产/记账”,要求:
- 可审计(audit)
- 可追溯(每笔来源、消耗、退款)
- 抗重复回调(webhook 重放)
- 支持退款/撤销/补发 这些都属于业务域问题。
4) 最佳实践:不要只存一个“余额”,要做“账本(Ledger)”
核心原则
- 账本是事实记录(不可随意改)
- 余额是账本的汇总视图(可缓存/可重算)
最小表结构(建议)
credit_ledger
iduser_idtype:grant | spend | refund | adjustmentamount(正负都可;或用delta)currency(如果你区分不同积分类型)source:stripe_payment_intent | apple_transaction | google_purchase | adminsource_id(例如 payment_intent_id / transaction_id,必须唯一以防重复入账)metadata(json,存套餐、订单号等)created_at
credit_balance(可选,做缓存)
user_idbalanceupdated_at
入账/扣减永远写 ledger;余额用事务更新或异步汇总。
5) 充值流程(以 Web/Stripe 为例)
标准流程
- 前端发起下单:
POST /checkout - 后端创建 Payment Intent / Checkout Session(带 idempotency key) (docs.stripe.com)
- 用户支付
- Stripe 发送 webhook:
payment_intent.succeeded(或 checkout.session.completed) - 你的 webhook handler:
- 校验签名
- 查
source_id是否已处理(幂等) - 写入
orders状态为 paid - 写入
credit_ledger一笔grant +N
- 返回给客户端:客户端刷新余额
注意:不要用前端“支付成功页面”作为入账依据。必须以 webhook 为准。 (MagicBell)
6) 退款/撤销如何处理?
退款会发生在:
- 用户申请退款
- 支付渠道拒付/争议(chargeback)
做法仍然是“记账”:
- webhook 收到
refund/charge.dispute相关事件 - 写
credit_ledger的refund(负数)或spend的反向冲销 - 余额随之变化
这就是为什么要用账本,而不是单字段余额。
7) 多端统一“积分/权益”的难点与可选工具
如果你未来同时支持:
- Web(Stripe)
- iOS(IAP)
- Android(Play Billing)
你会遇到“跨平台订阅/ 购买归一化”的问题:不同平台订单号、续费、退款、试用、家庭共享等规则都不同。
两条路:
路线 A:你自己做“购买归一化服务”
- 分别接三套 webhook/验证
- 统一映射到你自己的
orders/entitlements/ledger
路线 B:用第三方“订阅/内购中台”
例如 RevenueCat 这类服务提供跨平台购买数据与订阅状态管理,减少你自建复杂度。 (RevenueCat)
8) 你该怎么选(针对你当前阶段)
你在做的是早期多端同步工具,建议这样落地:
- 第一阶段(Web MVP)
- Stripe 收款
- 业务库做
credit_ledger - 后端用 webhook 入账(强幂等)
- 第二阶段(iOS/Android)
- iOS:优先 IAP
- Android:按目标市场与政策选择 Play Billing/外部支付策略(合规优先) (Google 帮助)
- 统一到你的
credit_ledger(source 分平台)
- 第三阶段(规模化)
- 如果订阅/跨平台很复杂,考虑上 RevenueCat 类中台 (RevenueCat)
如果你告诉我你设想的商业模型是:
- 订阅(Pro)还是
- 充值积分(可消耗) 我可以直接给你一份更具体的:
- 数据库 schema(orders、ledger、entitlements)
- webhook 事件清单(哪些事件必须处理)
- WebSocket 同步场景下的“余额一致性”处理策略(避免多端显示不一致)。